/*!
 * XSLT Templater - jQuery plugin for cached transforming XML with XSLT
 * <http://www.xslt-templater.com>
 * 
 * Version: 0.0.5
 * 
 * Copyright (c) 2010 Tsarev Oleg (<mailto:tsarev.oi@mail.ru>)
 * <http://live-scopes.blogspot.com>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
 * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 * USE OR OTHER DEALINGS IN THE SOFTWARE.
 * 
 */

/**
 * 
 * jQuery client-side XSLT Templater plugin.
 * 
 * @author <a href="mailto:tsarev.oi@mail.ru">Tsarev Oleg</a>
 * @version 0.0.5
 * 
 */

(function($) {
	
    $.fn.xslt = function() {
        return this;
    };
    
    // Boolean key: Firefox, Opera, Chrome, Safari OR InternetExplorer 
    var FOCS;
    
    if (window.DOMParser != undefined  && 
    	window.XSLTProcessor != undefined && 
    	window.XMLHttpRequest != undefined) {
    		
       var processor = new XSLTProcessor();
       
       if ($.isFunction(processor.transformDocument)) {
           support = window.XMLSerializer != undefined;
       } else {
           support = true;
       }
       
       if (support) {
           FOCS = true;
       }
    } else if (document.recalc) {
        FOCS = false;
    }
    
    if (FOCS != undefined) {

    	/*builder-open key="local|full"*/
    	
    	// Caches
    	/*import src="XSLTLocalCache.js" */
		$.getXSLTLocalCache = function (opt) {
			return new XSLTLocalCache(FOCS,opt);
		};
		
		/*builder-close*/
		
		$.getXSLTTemplaterInfo = function () {
			return {
				version:"0.0.5"
			};
		};
		
		/*import src="XSLTBaseCache.js" */
		
		/*builder-open key="base|full"*/
		var baseCache = new XSLTBaseCache();
		/*builder-close*/
		
		/*builder-open key="ajax|full"*/
		var ajaxCache = new XSLTBaseCache();
		/*builder-close*/
		
		var fixCache = new XSLTBaseCache();
		
		/*import src="DebugFunctions.js" */
		
    	// XSLT Processor
		/*import src="XSLTTransformer.js" */
		var transformer = new XSLTransformer(FOCS);
		
		// Browser different functions
		/*import src="TermFunctions.js" */
		
		// Inner util functions
		/*import src="InnerUtilFunctions.js" */
		
		// main context function
    	
    	var str = /^\s*</;
    	var completeValue = 4;
    	
    	$.fn.xslt = $.xslt = function(xml, xslt, callback, cache, caching) {
    		
    		var uin = new Date().getTime();
    		var target = ($.isFunction(this))?null:$(this);
    		
	        var transformed = false;
	        
	        /*builder-open key="base|ajax|full"*/
	        caching = normalizeCache(caching);
	        /*builder-close*/
	        
	        /*builder-open key="base|full"*/
	        var innerCache = baseCache;
	        /*builder-close*/
	        
	        /*builder-open key="ajax|full"*/
	        var netCache = ajaxCache;
	        /*builder-close*/
	        
	        var dnsCache = fixCache;
	        
	        /*builder-open key="base|full"*/
	        if (caching && caching.result) {
				if ((!checkObjectType(xml))&&(!checkObjectType(xslt))) {
					var value = innerCache.getElement(xml+"#"+xslt);
					if (value) {
						debug("Transforming result already cached!", target);
						
						if (target) target.html(value);
						
						/*builder-open key="local|full"*/
						trySetResultDoc(cache,value);
						/*builder-close*/
						call();
						
						if (target) return this;
						else return;
					}
				}	
			}
	        /*builder-close*/
	        
	        /*builder-open key="base|full"*/
	        // Inner cache keys
	        var cacheXMLKey = '';
	        var cacheXSLKey = '';
	        var cacheResultKey = '';
	        /*builder-close*/
	        
	        /*builder-open key="ajax|full"*/
	        // Inner cache ajax keys
	        var cacheXMLAKey = '';
	        var cacheXSLAKey = '';
	        /*builder-close*/
	        
	        // Ajax request keys for synchronization
	        var loadingXMLKey = false;
	        var loadingXSLKey = false;
	        
	        var xmlparameter = initXML(completeValue);
	    	var xslparameter = initXML(completeValue);
	        
			var nodes = makeNodes();
			includeElements(nodes.xmlnode,nodes.xslnode);
	        
			// Post calling functions
			/*import src="PostCallingFunctions.js" */
	    	
	    	var change = function() {
	    		
	    		loadingXMLKey = (!loadingXMLKey)?(xmlparameter.readyState == completeValue):true;
		        loadingXSLKey = (!loadingXSLKey)?(xslparameter.readyState == completeValue):true;
	    		
	    		trySetToFixCache(loadingXMLKey,xmlparameter,uin,dnsCache,"xml");
	    		trySetToFixCache(loadingXSLKey,xslparameter,uin,dnsCache,"xsl");
	    		
	    		if (loadingXMLKey && loadingXSLKey && !transformed) {
	    			
	    			xmlparameter = truncateXHR(xmlparameter,uin,dnsCache,"xml");
            		xslparameter = truncateXHR(xslparameter,uin,dnsCache,"xsl");
	    			
	        		transformer.calculate(target,xmlparameter,xslparameter,nodes,function(state){
	        			
	        			/*builder-open key="local|full"*/
	        			addToLocalCache(cache,state);
	        			/*builder-close*/
	        			
	        			/*builder-open key="base|full"*/
						addToBaseCache(caching,state);
						/*builder-close*/
						
						/*builder-open key="ajax|full"*/
						addToAjaxCache(xmlparameter,caching,cacheXMLAKey,state.xmlDoc);
						addToAjaxCache(xslparameter,caching,cacheXSLAKey,state.xslDoc);
						/*builder-close*/
						
						removeElements(nodes.xmlnode,nodes.xslnode);
						
						call();
						
	        		});
					transformed = true;
	        	}
	    	};
	        
	    	/*builder-open key="local|full"*/
	        if (cache) {
				cache.lock();
				cache.reset();
			}
	        /*builder-close*/

	        /*builder-open key="base|full"*/
			xml = tryExtractFromCache(xml,(caching && caching.xml),innerCache,target);
			/*builder-close*/
			
			if (checkObjectType(xml)) {
				xmlparameter = setFromCache(xmlparameter,xml);
	        } else if (str.test(xml)) {
	        	xmlparameter = setFromString(xmlparameter,xml,nodes.xmlnode);
	        	
	        	/*builder-open key="local|full"*/
				if (cache) {
			       	cache.setXmlDoc(getXMLRepresentation(xmlparameter));
			    }
				/*builder-close*/
				
				/*builder-open key="base|full"*/
				cacheXMLKey = setIf(caching && caching.xml,xml,'');
				/*builder-close*/
				
	    	} else {
	    		
	    		/*builder-open key="base|full"*/
	    		cacheXMLKey = setIf(caching && caching.xml,xml,'');
	    		/*builder-close*/
	    		
	    		/*builder-open key="ajax|full"*/
	    		var value = false;
		    	var uploadKey = true;
		    	if (caching && caching.ajax) {
        			cacheXMLAKey = xml;
        			value = netCache.getElement(cacheXMLAKey);
        			if (value) {
        				uploadKey = checkByCache(value.cache,uploadKey);
        				uploadKey = checkByExpires(value.expires,uploadKey);
        				if (!uploadKey) {
        					debug("Ajax resource (" + xml + ") - already cached!",target);
        					xmlparameter.responseXML = value.value;
        				}
        			}
    			}
		    	if (!value || uploadKey) {
		    	/*builder-close*/
		    		
    				xmlparameter = setFromFile(xmlparameter,xml,change,nodes.xmlnode);
    				
    			/*builder-open key="ajax|full"*/
    			}
		    	/*builder-close*/
		    	
	    	}
			
			/*builder-open key="base|full"*/
			xslt = tryExtractFromCache(xslt,(caching && caching.xsl),innerCache,target);
			/*builder-close*/
			
			if (checkObjectType(xslt)) {
				xslparameter = setFromCache(xslparameter,xslt);
				change();
		    } else if (str.test(xslt)) {
		    	xslparameter = setFromString(xslparameter,xslt,nodes.xslnode);
		    	
		    	/*builder-open key="local|full"*/
				if (cache) {
		        	cache.setXslDoc(getXMLRepresentation(xslparameter));
		        }
				/*builder-close*/
				
				/*builder-open key="base|full"*/
				cacheXSLKey = setIf(caching && caching.xsl,xslt,'');
				cacheResultKey = setIf(caching && caching.result,xml+"#"+xslt,'');
				/*builder-close*/
				
				change();
		    } else {
		    	/*builder-open key="base|full"*/
		    	cacheXSLKey = setIf(caching && caching.xsl,xslt,'');
		    	cacheResultKey = setIf(caching && caching.result,xml+"#"+xslt,'');
		    	/*builder-close*/
		    	
		    	/*builder-open key="ajax|full"*/
		    	var value = false;
		    	var uploadKey = true;
		    	if (caching && caching.ajax) {
        			cacheXSLAKey = xslt;
        			value = netCache.getElement(cacheXSLAKey);
        			if (value) {
        				uploadKey = checkByCache(value.cache,uploadKey);
        				uploadKey = checkByExpires(value.expires,uploadKey);
        				if (!uploadKey) {
        					debug("Ajax resource (" + xslt + ") - already cached!",target);
        					xslparameter.responseXML = value.value;
        					change();
        				}
        			}
    			}
		    	if (!value || uploadKey) {
		    	/*builder-close*/
		    		
    				xslparameter = setFromFile(xslparameter,xslt,change,nodes.xslnode);
    				callbackForLocal(xslparameter);
    				
    			/*builder-open key="ajax|full"*/
    			}
		    	/*builder-close*/
		    	
		    }
			
			if (target) return this;
			else return;
		};
	}

})(jQuery);
